﻿Public Class Filter

	Public Filters As New List(Of Criteria)
	Public MustMeasure As Boolean


	Dim Rs As Recordset
	Property Recordset As Recordset
		Get
			Return Rs
		End Get
		Set(value As Recordset)
			Rs = value
			Invalidate()
			If Recordset Is Nothing Then Return
			For Each Item As Criteria In Filters
				Item.CreateFilters(Rs)
				Item.RenewCounts(Rs)
			Next
			MustMeasure = True
		End Set
	End Property

	Dim _FilteredResults As Recordset
	Property FilteredResults As Recordset
		Get
			If _FilteredResults Is Nothing Then Return Recordset
			Return _FilteredResults
		End Get
		Set(Value As Recordset)
			_FilteredResults = Value
		End Set
	End Property

	Sub New()
		InitializeComponent()
		DoubleBuffered = True
		Themes.Register(Me)
	End Sub

	Dim _Active As Rectangle
	Property Active As Rectangle
		Get
			Return _Active
		End Get
		Set(Value As Rectangle)
			If _Active = Value Then Return
			Invalidate(_Active)
			_Active = Value
			Invalidate(_Active)
			Cursor = If(_Active = Nothing, Cursors.Default, Cursors.Hand)
		End Set
	End Property

	Sub Add(FilterName As String, Field As String, Conversions As String)
		Dim Filter As New Criteria With {.Parent = Me, .Name = FilterName, .Field = Field}
		Filters.Add(Filter)
		If Conversions <> "" Then Filter.SetConversions(Conversions)
		Visible = True
		MustMeasure = True
	End Sub

	Class Criteria

		Public Name As String
		Public Field As String
		Public Options As String
		Public Parent As Filter
		Public Expanded As Boolean = True
		Dim RectHeader As Rectangle
		Dim RectClose As Rectangle
		Dim RectSort As Rectangle
		Dim RectExpand As Rectangle

		ReadOnly Property Display(key As Object) As String
			Get
				If key = Nothing Then Return "(Boş)"
				If Conversions.Count = 0 Then Return key
				If Not Conversions.ContainsKey(key) Then Return key
				Return Conversions(key)
			End Get
		End Property

		Dim Rects As New List(Of Rectangle)

		Dim _Sort As String = ""
		Private Property Sort As String
			Get
				Return _Sort
			End Get
			Set(Value As String)
				_Sort = Value
				Parent.MustMeasure = True
				Parent.Invalidate()
			End Set
		End Property

		Dim HasCheckedItems As Boolean

		Sub Measure(ByRef y As Integer, w As Integer)
			HasCheckedItems = False
			For Each Item As FilterItem In FilterItems
				If Item.Checked Then HasCheckedItems = True
			Next
			Rects.Clear()
			RectClose = Nothing
			RectSort = Nothing
			RectHeader = New Rectangle(0, y, w - 0, 20)
			RectExpand = New Rectangle(0, y, RectHeader.Height, RectHeader.Height)
			Rects.Add(RectHeader)
			y += RectHeader.Height
			RectSort = New Rectangle(RectHeader.Right - RectHeader.Height + 1, RectHeader.Top + 1, RectHeader.Height - 2, RectHeader.Height - 2)
			Rects.Add(RectSort)
			If Expanded Then
				Dim A() As FilterItem = FilterItems.ToArray
				Array.Sort(A)
				For i = 0 To A.Length - 1
					Dim Item As FilterItem = A(i)
					Item.Measure(y, w)
					Rects.Add(Item.Rect)
				Next
			End If
			y += 1
		End Sub

		Sub Draw(g As Graphics)
			Dim R As New Rectangle(Rects(0).Left, Rects(0).Top, Rects(0).Width, Rects(Rects.Count - 1).Bottom - Rects(0).Top + 2)
			R.Inflate(-2, -2)
			g.PixelOffsetMode = Drawing2D.PixelOffsetMode.None
			g.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
			R = RectHeader
			g.FillRectangle(New SolidBrush(Color.FromArgb(128, Color.Black)), R)
			g.SmoothingMode = Drawing2D.SmoothingMode.None
			g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
			R.Offset(RectExpand.Width - 2, 0)
			R.Width -= RectExpand.Width - 2
			R.Inflate(-2, -2)
			DrawStringShadowed(g, Name, Parent.Font, Color.White, R, StrFormat("lm"))
			R = RectSort
			R.Inflate(-2, -2)
			If Sort = "2" Then
				FillRoundRect(g, R, New SolidBrush(Color.FromArgb(192, Color.White)), 6)
			ElseIf Parent.Active = RectSort Then
				FillRoundRect(g, R, New SolidBrush(Color.FromArgb(64, Color.White)), 6)
			End If
			R.Inflate(-2, -2)
			'DrawImage(g, My.Resources.sort, R)
			R = RectExpand
			R.Inflate(-2, -2)
			Dim Path As New Drawing2D.GraphicsPath
			If Expanded Then
				R.Inflate(-4, -5)
				Path.AddLines(New Point() {New Point(R.Left, R.Top), New Point(R.Right, R.Top), New Point(R.Left + R.Width / 2, R.Bottom)})
			Else
				R.Inflate(-5, -4)
				Path.AddLines(New Point() {New Point(R.Left, R.Top), New Point(R.Right, R.Top + R.Height / 2), New Point(R.Left, R.Bottom)})
			End If
			g.FillPath(New SolidBrush(Color.FromArgb(If(RectExpand = Parent.Active, 255, 192), Color.White)), Path)
			If Expanded Then
				For Each Item In FilterItems
					Item.Draw(g)
				Next
			End If
		End Sub

		Sub ApplyFilters(ByRef Where As String)
			Dim Text As String = ""
			For Each Item As FilterItem In FilterItems
				If Item.Checked Then
					If Item.Value Is Nothing OrElse IsDBNull(Item.Value) Then
						Text &= " or " & Field & " is null"
					Else
						Text &= " or " & Field & " = '" & Item.Value & "'"
					End If
				End If
			Next
			If Text = "" Then Return
			Where &= "and (" & Mid(Text, 5) & ")"
		End Sub

		Sub RenewCounts(Rs As Recordset)
			Dim Rsg As Recordset = Rs.GroupBy(Field & ",count(*)")
			For Each Item As FilterItem In FilterItems
				Item.Count = 0
			Next
			Do Until Rsg.EOF
				For Each Item As FilterItem In FilterItems
					If Trim(IsNull(Item.Value, "")) = Trim(IsNull(Rsg(Field), "")) Then Item.Count = Rsg("count")
				Next
				Rsg.MoveNext()
			Loop
		End Sub

		Dim FilterItems As New List(Of FilterItem)

		Class FilterItem
			Implements IComparable(Of FilterItem)

			Public Value As Object
			Public Count As Integer
			Public Criteria As Criteria
			Public Rect As Rectangle
			Public RectCheck As Rectangle
			Public Checked As Boolean = False

			Sub New(Criteria As Criteria, Value As Object)
				Me.Criteria = Criteria
				Me.Value = Value
			End Sub

			Sub Measure(ByRef y As Integer, w As Integer)
				Rect = New Rectangle(0, y, w - 0, 18)
				RectCheck = New Rectangle(Rect.Left, Rect.Top, Rect.Height, Rect.Height)
				y += Rect.Height
			End Sub

			Sub Draw(g As Graphics)
				Dim col As Color = Color.DarkRed
				Dim R As Rectangle = Rect
				g.SmoothingMode = Drawing2D.SmoothingMode.None
				If Checked Then g.FillRectangle(New SolidBrush(Color.FromArgb(128, Color.White)), R)
				R = RectCheck
				R.Inflate(-3, -3)
				g.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
				g.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
				g.FillRoundRect(R, New SolidBrush(Color.FromArgb(If(Count = 0, 32, If(Rect = Criteria.Parent.Active, 192, 128)), Color.Black)), 2)
				g.SmoothingMode = Drawing2D.SmoothingMode.None
				R.Inflate(-1, -1)
				'g.FillRectangle(Brushes.White, R)
				R.Inflate(-1, -1)
				g.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
				g.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
				If Checked Then
					R.Inflate(-1, -1)
					R.Height -= 1
					Dim Pen As New Pen(Brushes.White, 2)
					g.DrawLines(Pen, New Point() {New Point(R.Left, R.Top + R.Height / 2), New Point(R.Left + R.Width / 3, R.Bottom), New Point(R.Right, R.Top)})
				End If
				R = Rect
				R.Offset(RectCheck.Width, 0)
				R.Width -= RectCheck.Width
				Dim Text As String = Criteria.Display(IsNull(Value, "(Boş)")).Trim
				Dim w = g.MeasureString(Text, Font).Width
				If R.Left + w > R.Width - 12 Then w = R.Width - 12 - R.Left
				Dim F As StringFormat = StrFormat("lm")
				F.Trimming = StringTrimming.EllipsisWord
				g.DrawString(Text, Font, If(Active, col.Brush, Color.Black.Opacity(If(Count = 0, 128, 255)).Brush), R, F)
				R.Width -= 3
				If Criteria.HasCheckedItems Then
					If Checked Then g.DrawString("(" & Count & ")", Font, New SolidBrush(col), R, StrFormat("rm"))
				Else
					g.DrawString("(" & Count & ")", Font, New SolidBrush(col), R, StrFormat("rm"))
				End If
			End Sub

			ReadOnly Property Active As Boolean
				Get
					Return Criteria.Parent.Active = Rect
				End Get
			End Property

			Shared ReadOnly Property Font As Font
				Get
					Return New Font("verdana", 7)
				End Get
			End Property

			Function MouseMove(p As Point, Clicked As Boolean) As Boolean
				If Not Rect.Contains(p) Then Return False
				If Count = 0 Then Return False
				If Not Clicked Then
					Criteria.Parent.Active = Rect
				Else
					Checked = Not Checked
					Criteria.Parent.ApplyFilters()
				End If
				Return True
			End Function

			Public Function CompareTo(other As FilterItem) As Integer Implements System.IComparable(Of FilterItem).CompareTo
				If IsNull(Value) And IsNull(other.Value) Then Return 0
				If IsNull(Value) Then Return 1
				If IsNull(other.Value) Then Return -1
				Select Case Criteria.Sort
					Case "2" : Return If(Count > other.Count, -1, If(Count = other.Count, 0, 1))
					Case Else : Return If(IsNull(Value, "") > IsNull(other.Value, ""), 1, If(IsNull(Value, "") = IsNull(other.Value, ""), 0, -1))
				End Select
			End Function

		End Class

		Sub SetConversions(Text As String)
			For Each Item As String In Text.Split(",")
				Dim Data = Item.Split(":")
				Conversions.Add(Data(0), Data(1))
			Next
		End Sub

		Dim Conversions As New SortedList(Of String, String)

		Function MouseMove(p As Point, Clicked As Boolean) As Boolean
			If Clicked Then
				If RectExpand.Contains(p) Then If Clicked Then Expanded = Not Expanded : Parent.MustMeasure = True : Parent.Invalidate() : Return True
				If RectSort.Contains(p) Then Sort = If(Sort <> "2", "2", "1") : Return True
			Else
				If RectExpand.Contains(p) Then Parent.Active = RectExpand : Return True
				If RectSort.Contains(p) Then Parent.Active = RectSort : Return True
			End If
			If Expanded Then
				For Each Item As FilterItem In FilterItems
					If Item.MouseMove(p, Clicked) Then Return True
				Next
			End If
		End Function

		Sub CreateFilters(Rs As SSD.Recordset)
			FilterItems.Clear()
			Dim Rsg As Recordset = Rs.GroupBy(Field)
			Do Until Rsg.EOF
				FilterItems.Add(New FilterItem(Me, Rsg(Field)))
				Rsg.MoveNext()
			Loop
		End Sub

	End Class

	Protected Overrides Sub OnPaint(e As System.Windows.Forms.PaintEventArgs)
		If MustMeasure Then Measure()
		Dim R As Rectangle = ClientRectangle
		e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.None
		e.Graphics.FillRectangle(Themes.Current.FormColor.Mix(Color.White, 50).Brush, R)
		e.Graphics.DrawLine(New Pen(Color.FromArgb(64, Color.Black)), R.Right - 1, R.Top, R.Right - 1, R.Bottom)
		e.Graphics.TranslateTransform(0, AutoScrollPosition.Y)
		e.Graphics.SmoothingMode = Drawing2D.SmoothingMode.HighQuality
		e.Graphics.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
		e.Graphics.TextRenderingHint = Drawing.Text.TextRenderingHint.ClearTypeGridFit
		For Each Item As Criteria In Filters
			Item.Draw(e.Graphics)
		Next
	End Sub

	Protected Overrides Sub OnResize(e As System.EventArgs)
		MyBase.OnResize(e)
		MustMeasure = True
		Invalidate()
	End Sub

	Dim Header As Rectangle

	Sub Measure()
		MustMeasure = False
		Dim w As Integer = ClientRectangle.Width
		Dim y As Integer = 0
		For Each Item As Criteria In Filters
			Item.Measure(y, w)
		Next
		AutoScrollMinSize = New Size(0, y)
	End Sub

	Protected Overrides Sub OnMouseLeave(e As System.EventArgs)
		MyBase.OnMouseLeave(e)
		Active = Nothing
	End Sub

	Protected Overrides Sub OnMouseMove(e As System.Windows.Forms.MouseEventArgs)
		MyBase.OnMouseMove(e)
		For Each Item As Criteria In Filters
			If Item.MouseMove(e.Location, False) Then Return
		Next
		Active = Nothing
	End Sub

	Protected Overrides Sub OnMouseDown(e As System.Windows.Forms.MouseEventArgs)
		MyBase.OnMouseDown(e)
		Dim P As Point = e.Location
		P.Offset(0, -AutoScrollPosition.Y)
		For Each Item As Criteria In Filters
			If Item.MouseMove(P, True) Then Return
		Next
		Active = Nothing
	End Sub

	Sub Restart()
		CType(Parent, Box).Process.Report.Restart()
	End Sub

	Dim Where As String = ""

	Sub ApplyFilters()
		MustMeasure = True
		Where = ""
		For Each Item As Criteria In Filters
			Item.ApplyFilters(Where)
		Next
		If Where = "" Then
			FilteredResults = Recordset
		Else
			FilteredResults = Recordset.Select(Mid(Where, 5))
		End If
		If Not FilteredResults Is Nothing Then
			Dim Rs As Recordset = FilteredResults
			For Each Item As Criteria In Filters
				Item.RenewCounts(Rs)
			Next
		End If
		FilteredResults.MoveFirst()
		CType(Parent, Box).ApplyFilters()
		Invalidate()
	End Sub

	Protected Overrides Sub OnScroll(se As System.Windows.Forms.ScrollEventArgs)
		MyBase.OnScroll(se)
		Invalidate()
	End Sub

	Protected Overrides Sub OnMouseWheel(e As System.Windows.Forms.MouseEventArgs)
		If Not ClientRectangle.Contains(e.Location) Then Return
		MyBase.OnMouseWheel(e)
	End Sub

	Sub Reset()
		Recordset = Nothing
		Filters.Clear()
		FilteredResults = Nothing
		Invalidate()
	End Sub

End Class
